#include "SceneObject.h"


SceneObject::SceneObject(  )
{
	selectedMesh = -1;
	selectedTri = -1;
	_selectedTriData = NULL;

	_objData = NULL;
	_mat = new Matrix3();
	_worldObject = true;
	//cout <<"SceneObject( ObjectDatabase* db, RawObjectData *objData )# END"<<endl;
}


SceneObject::SceneObject( RawObjectData *objData  )
{
	selectedMesh = -1;
	selectedTri = -1;
	_selectedTriData = NULL;

	// daten in die datenbank einfügen, zurückgegeben wird das zu benutzende objekt
	RawObjectData* data = objData;//ISceneManagement::addRawObjectData( objData );
	_objData = data;
	_mat = new Matrix3();
	_worldObject = true;
	fillCollisionPlanes();
	//cout <<"SceneObject( ObjectDatabase* db, RawObjectData *objData )# END"<<endl;
}




SceneObject::~SceneObject( )
{
	cout <<"_~SceneObject()_"<<endl;
	if( _collPlanes != NULL )
		delete _collPlanes;
	if( _mat != NULL )
		delete _mat;
	if( _objData != NULL )
		delete _objData;
}

void SceneObject::fillCollisionPlanes()
{
	_collPlanes = new list<TrianglePlane*>;
	//cout <<"SceneObject::fillCollisionPlanes()# 1"<<endl;
	if( _objData == NULL )
		throw "RawObjectData in SceneObject::fillCollisionPlanes() is NULL Pointer";

	for( vector<MeshData*>::iterator it = _objData->getMeshes()->begin();it != _objData->getMeshes()->end();it++ )
	{
		//cout <<"SceneObject::fillCollisionPlanes()# 2"<<endl;
		if( (*it) == NULL )
			throw "vector<MeshData*>::iterator it in SceneObject::fillCollisionPlanes() is NULL Pointer";
		for( vector<TriangleData*>::iterator it2 = (*it)->_triangles.begin();it2 != (*it)->_triangles.end();it2++ )
		{
			//cout <<"SceneObject::fillCollisionPlanes()# 3| "<<(*it)<<endl;
			if( (*it2) == NULL )
				throw "vector<MeshData*>::iterator it2 in SceneObject::fillCollisionPlanes() is NULL Pointer";
			//cout <<"SceneObject::fillCollisionPlanes()# 4"<<endl;
			if( (*it2)->isCollision() )
			{
				//cout <<"SceneObject::fillCollisionPlanes()# add Collision Triangle"<<endl;

				// TODO Call TriangleData function "CreateEdges" to create all possible edges for this tri

				Vector3* p1;
				Vector3* p2;
				Vector3* p3;

				map<VertexData*,Vector3*>::iterator currP = _collPoints.find( (*it2)->getP1() );
				// search for Point1
				if( currP == _collPoints.end() )
				{
					p1 = new Vector3( *((*it2)->getP1()->getPoint()) );
					_collPoints.insert( pair<VertexData*,Vector3*>((*it2)->getP1(), p1 ) );
				}
				else
				{
					p1 = currP->second;
				}

				currP = _collPoints.find( (*it2)->getP2() );
				// search for Point2
				if( currP == _collPoints.end() )
				{
					p2 = new Vector3( *((*it2)->getP2()->getPoint()) );
					_collPoints.insert( pair<VertexData*,Vector3*>((*it2)->getP2(), p2 ) );
				}
				else
				{
					p2 = currP->second;
				}

				currP = _collPoints.find( (*it2)->getP3() );
				// search for Point3
				if( currP == _collPoints.end() )
				{
					p3 = new Vector3( *((*it2)->getP3()->getPoint()) );
					_collPoints.insert( pair<VertexData*,Vector3*>((*it2)->getP3(), p3 ) );
				}
				else
				{
					p3 = currP->second;
				}

				_collPlanes->push_back(
									   new TrianglePlane(p1,p2,p3, (*it2)->getNormal(),
														 new EdgeInfo( ((*it2)->getEdge1()->getNormal()), (*it2)->getEdge1()->isSmooth() ),
														 new EdgeInfo( ((*it2)->getEdge2()->getNormal()), (*it2)->getEdge2()->isSmooth() ),
														 new EdgeInfo( ((*it2)->getEdge3()->getNormal()), (*it2)->getEdge3()->isSmooth() )
														 )
									   );
			}
		}
	}
	//cout <<"SceneObject::fillCollisionPlanes()# 5"<<endl;
}

// transformiert das objekt mit der eigene matrix
//////////////////////////////////////////////////////////////////////////////
void SceneObject::TransformObject()
{
	if( !_worldObject )
		glLoadIdentity();
	float *matEle = _mat->getElementArray();
	glMultMatrixf(matEle);
	delete []matEle;
}

// objekt wird hier auschließlich gezeichnet
//////////////////////////////////////////////////////////////////////////////
void SceneObject::DrawObject()
{
	//cout <<"___CPU_RAM_RENDERING___"<<endl;
	//cout<<"render_obj: "<<_objData->getFilename()<<endl;
	int maxMeshes = _objData->getMeshes()->size();


	for( int i = 0; i < maxMeshes;i++ )
	{
		DrawObject( i );
	}
}

void SceneObject::DrawObject( unsigned int meshIndex )
{
	//cout <<"___CPU_RAM_RENDERING___"<<endl;
	//cout<<"render_obj: "<<_objData->getFilename()<<endl;
	if( meshIndex < 0 || meshIndex >= _objData->getMeshes()->size() )
		return;

	MeshData *mesh;
	TriangleData *tri;
	MaterialData *mat;
	TextureData *tex;
	int maxTris;


//cout <<"meshes"<<endl;
		mesh = _objData->getMeshes()->at(meshIndex);
		maxTris = mesh->_triangles.size();
//		if( _objData->isAlwaysVisible() )
//			glDepthFunc( GL_ALWAYS );

		tri = mesh->_triangles.at(0);
		if( tri->isWithTexture())
			tri->BindTexture();



		for( int j = 0; j < 	maxTris;j++ )
		{
			//cout <<"tri "<<j<<endl;
			tri = mesh->_triangles.at(j);
			if( tri->isInvisible() )
				continue;

			mat = tri->getMaterial();
			if( mat != NULL )
			{
				tex = tri->getMaterial()->getTexture();

				if( tri->IsTransparency() )
				{
					glDisable(GL_LIGHTING );
					glEnable( GL_BLEND );
				}

				//glColorMaterial ( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE ) ;
				//glEnable ( GL_COLOR_MATERIAL ) ;

				//~ glMaterialfv(GL_FRONT,	GL_AMBIENT,		mat->getAmbient()->_rgba);
				//~ glMaterialfv(GL_FRONT,	GL_DIFFUSE,		mat->getDiffuse()->_rgba);

				//~ glMaterialfv(GL_FRONT,	GL_SPECULAR,	mat->getSpecular()->_rgba);
				//~ glMaterialf(GL_FRONT,	GL_SHININESS,	7.0);
			}


			if( mat != NULL && tri->isWithTexture() )
			{
				//cout << "Rendering With Texture"<<endl;
				//~ glEnable (GL_TEXTURE_2D);
				//~ tri->BindTexture();
				glBegin(GL_TRIANGLES);
					glColor4fv( mat->getAmbient()->_rgba );
					glTexCoord2f( *tri->getTexCoord3()->getU(), *tri->getTexCoord3()->getV() );
					glNormal3f(tri->getP3Normal()->getX(),tri->getP3Normal()->getY(),tri->getP3Normal()->getZ());
					glVertex3f( tri->getP3()->getPoint()->getX(), tri->getP3()->getPoint()->getY(),tri->getP3()->getPoint()->getZ() );

					glTexCoord2f( *tri->getTexCoord2()->getU(), *tri->getTexCoord2()->getV() );
					glNormal3f(tri->getP2Normal()->getX(),tri->getP2Normal()->getY(),tri->getP2Normal()->getZ());
					glVertex3f( tri->getP2()->getPoint()->getX(), tri->getP2()->getPoint()->getY(),tri->getP2()->getPoint()->getZ() );

					glTexCoord2f( *tri->getTexCoord1()->getU(), *tri->getTexCoord1()->getV() );
					glNormal3f(tri->getP1Normal()->getX(),tri->getP1Normal()->getY(),tri->getP1Normal()->getZ());
					glVertex3f( tri->getP1()->getPoint()->getX(), tri->getP1()->getPoint()->getY(),tri->getP1()->getPoint()->getZ() );

				glEnd();
				//~ glDisable(GL_TEXTURE_2D);
			}
			else
			{
				//cout << "Rendering Without Texture "<<tri->isWithTexture()<<endl;
				glBegin(GL_TRIANGLES);
					glColor4fv( mat->getAmbient()->_rgba );
					glNormal3f(tri->getP3Normal()->getX(),tri->getP3Normal()->getY(),tri->getP3Normal()->getZ());
					glVertex3f( tri->getP3()->getPoint()->getX(), tri->getP3()->getPoint()->getY(),tri->getP3()->getPoint()->getZ() );

					glNormal3f(tri->getP2Normal()->getX(),tri->getP2Normal()->getY(),tri->getP2Normal()->getZ());
					glVertex3f( tri->getP2()->getPoint()->getX(), tri->getP2()->getPoint()->getY(),tri->getP2()->getPoint()->getZ() );

					glNormal3f(tri->getP1Normal()->getX(),tri->getP1Normal()->getY(),tri->getP1Normal()->getZ());
					glVertex3f( tri->getP1()->getPoint()->getX(), tri->getP1()->getPoint()->getY(),tri->getP1()->getPoint()->getZ() );
				glEnd();
			}

			if( mat != NULL && tri->IsTransparency() )
			{
				glEnable(GL_LIGHTING );
				glDisable( GL_BLEND );
			}
			//~ glDisable ( GL_COLOR_MATERIAL ) ;
		}


		tri = mesh->_triangles.at(0);
		if( tri->isWithTexture())
			tri->UnbindTexture();

//		if( _objData->isAlwaysVisible() )
//			glDepthFunc( GL_LESS );
}

void SceneObject::DrawSelection()
{
	if( _selectedTriData == NULL )
		return;
	//cout <<"DRAW_SELECTION "<<_selectedTriData<<endl;
	TriangleData *tri = _selectedTriData;

	glDisable(GL_LIGHTING);
	glColor3f(1.0,0.0,0.0);
	glBegin(GL_TRIANGLES);
		glVertex3f(
							tri->getP1()->getPoint()->getX(),
							tri->getP1()->getPoint()->getY(),
							tri->getP1()->getPoint()->getZ()  );
			glVertex3f(
							tri->getP2()->getPoint()->getX() ,
							tri->getP2()->getPoint()->getY() ,
							tri->getP2()->getPoint()->getZ() );

			glVertex3f(
							tri->getP3()->getPoint()->getX(),
							tri->getP3()->getPoint()->getY(),
							tri->getP3()->getPoint()->getZ() );
	glEnd();
	glBegin(GL_LINES);

			glVertex3f(
							tri->getMiddlePoint()->getX(),
							tri->getMiddlePoint()->getY(),
							tri->getMiddlePoint()->getZ()  );
			glVertex3f(
							tri->getMiddlePoint()->getX() + tri->getNormal()->getX(),
							tri->getMiddlePoint()->getY() + tri->getNormal()->getY(),
							tri->getMiddlePoint()->getZ() + tri->getNormal()->getZ() );
	glEnd();


	glEnable(GL_LIGHTING);
}

//rendert dieses objekt
void SceneObject::RenderObject()
{

	//cout <<"SceneObject::RenderObject()"<<endl;


	//cout << ">> TIME_ELAPSED_FOR_CPU: "<<Utils::GET_ELAPSED_TIME (	)<<endl;
	glPushMatrix();// matrix only set for this object
		TransformObject();
		//Utils::PRINT_ELAPSED_TIME( ">> TIME_ELAPSED_FOR_CPU: ", 30 );
		DrawObject();
		//Utils::PRINT_ELAPSED_TIME( ">> TIME_ELAPSED_FOR_GPU: ", 30 );
		//DrawSelection();

		//render all child objects under this
		RenderChildObjects ();
	glPopMatrix();

	GLenum error;
	while ( (error = glGetError() ) != GL_NO_ERROR )
	{
		cout <<"GL_ERROR at SceneObject::RenderObject()[  "<< gluErrorString(error) << " ]"<<endl;
	}

	//cout << ">> TIME_ELAPSED_FOR_GPU: "<<Utils::GET_ELAPSED_TIME (	)<<endl;
};


Matrix3 SceneObject::getMatrix() const
{
	return *_mat;
}

void SceneObject::setMatrix(const Matrix3& mat)
{
	//cout <<"SceneObject::setMatrix( const Matrix3& mat )"<<endl;
	_mat->setElements(mat.getElements() );

	setTransformation( mat );
}

void SceneObject::setTransformation( const Matrix3& mat )
{
	// TRANSFORMATION ALSO FOR COLLISION DATA
	/////////////////////////////////////////
		for( map<VertexData*,Vector3*>::iterator it = _collPoints.begin();it != _collPoints.end();it++ )
			*(it->second) = mat * (*(it->first->getPoint()));

		//cout <<"_collPlanes::Size# "<< _collPlanes.size()<<endl;
		for( list<TrianglePlane*>::iterator it = _collPlanes->begin();it != _collPlanes->end();it++ )
			(*it)->transform( mat );

		CollisionHul::setTransformation( mat );

	for( list<SceneNode*>::iterator it = getChildNodes ()->begin();it != getChildNodes ()->end();it++ )
	{
		SceneObject* scnObj = dynamic_cast<SceneObject*>(*it);
		if( scnObj )
			scnObj->setTransformation( mat );
	}
}

long SceneObject::getPolygonNum ()
{
	return _objData->getPolygonCount();
}

RawObjectData* SceneObject::getRawData()
{
	return _objData;
};

bool SceneObject::isWorldObject()
{
	return _worldObject;
};

void SceneObject::setWorldObject( bool wo )
{
	_worldObject = wo;
};


list<TrianglePlane*>* SceneObject::getTrianglePlaneList()
{
	return _collPlanes;
}

void SceneObject::SelectNextTriangle()
{

	int maxMeshes = _objData->getMeshes()->size();
	int maxTris;
	MeshData *mesh;
	TriangleData* tri;


	if( selectedMesh < 0 )
		selectedMesh = 0;

	mesh = _objData->getMeshes()->at(selectedMesh);

	if( selectedTri < 0 )
		selectedTri = 0;
	else
	{
		selectedTri++;
	}

	maxTris = mesh->_triangles.size();

	if( selectedTri >= maxTris )
	{
		selectedTri = 0;
		selectedMesh++;
		if( selectedMesh >= maxMeshes )
		{
			selectedMesh = 0;
			mesh = _objData->getMeshes()->at(selectedMesh);
		}
	}

	tri = mesh->_triangles.at(selectedTri);
	_selectedTriData = tri;
	cout<<"next mesh: "<<selectedMesh<<endl;
	cout<<"next tri: "<<selectedTri<<endl;
}

void SceneObject::SelectPreviousTriangle()
{

	int maxMeshes = _objData->getMeshes()->size();
	int maxTris;
	MeshData *mesh;
	TriangleData* tri;


	if( selectedMesh < 0 )
		selectedMesh = maxMeshes-1;

	mesh = _objData->getMeshes()->at(selectedMesh);
	maxTris = mesh->_triangles.size();

	if( selectedTri < 0 )
		selectedTri = maxTris-1;
	else
	{
		selectedTri--;
	}


	tri = mesh->_triangles.at(selectedTri);
	_selectedTriData = tri;
	cout<<"next mesh: "<<selectedMesh<<endl;
	cout<<"next tri: "<<selectedTri<<endl;
}





